package vip.xiaonuo.modular.business.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xkcoding.http.util.StringUtil;
import lombok.extern.java.Log;
import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import vip.xiaonuo.core.context.login.LoginContextHolder;
import vip.xiaonuo.core.dbs.CurrentDataSourceContext;
import vip.xiaonuo.core.exception.ServiceException;
import vip.xiaonuo.core.factory.PageFactory;
import vip.xiaonuo.core.pojo.login.SysLoginUser;
import vip.xiaonuo.core.pojo.page.PageResult;
import vip.xiaonuo.dbs.core.annotation.DataSource;
import vip.xiaonuo.modular.business.entity.BiUserStore.BiUserStoreVo;
import vip.xiaonuo.modular.business.entity.BiUserStore.StoreBusiVo;
import vip.xiaonuo.modular.business.param.BiUserStore.QueryParam;
import vip.xiaonuo.modular.business.param.StoreQueryParam;
import vip.xiaonuo.modular.business.service.RegressionAnalysisService;
import vip.xiaonuo.modular.business.service.UserStoreService;
import vip.xiaonuo.modular.common.DistanceHelper;
import vip.xiaonuo.modular.common.GridColorUtils;
import vip.xiaonuo.modular.common.WdAnalysisConst;
import vip.xiaonuo.modular.manage.biUserStore.entity.BiUserStore;
import vip.xiaonuo.modular.manage.biUserStore.param.BiUserStoreParam;
import vip.xiaonuo.modular.manage.biUserStore.service.BiUserStoreService;
import vip.xiaonuo.modular.manage.biapplication.entity.BiApplication;
import vip.xiaonuo.modular.manage.biapplication.service.BiApplicationService;
import vip.xiaonuo.modular.manage.bicouponwrite.entity.BiCouponWrite;
import vip.xiaonuo.modular.manage.bicouponwrite.mapper.BiCouponWriteMapper;
import vip.xiaonuo.modular.manage.bicouponwrite.param.BiCouponWriteParam;
import vip.xiaonuo.modular.manage.bicouponwrite.service.BiCouponWriteService;
import vip.xiaonuo.modular.manage.biindexdata.entity.BiIndexData;
import vip.xiaonuo.modular.manage.biindexdata.mapper.BiIndexDataMapper;
import vip.xiaonuo.modular.manage.biindexdata.service.BiIndexDataService;
import vip.xiaonuo.modular.manage.bioperateddata.entity.BiOperatedData;
import vip.xiaonuo.modular.manage.bioperateddata.service.BiOperatedDataService;
import vip.xiaonuo.modular.manage.biuserbusiness.entity.BiUserBusiness;
import vip.xiaonuo.modular.manage.biuserbusiness.entity.BusinessInfo;
import vip.xiaonuo.modular.manage.biuserbusiness.service.BiUserBusinessService;
import vip.xiaonuo.modular.manage.poidata.entity.SubTagTotal;
import vip.xiaonuo.modular.manage.poidata.mapper.BiPoiDataMapper;
import vip.xiaonuo.modular.manage.poidata.param.PoiDataParam;
import vip.xiaonuo.modular.manage.poidatatags.mapper.PoiDataTagsJoinTypeMapper;
import vip.xiaonuo.modular.manage.storeorder.entity.BiStoreOrder;
import vip.xiaonuo.modular.manage.storeorder.mapper.BiStoreOrderMapper;
import vip.xiaonuo.modular.manage.storeorder.param.BiStoreOrderParam;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author cy
 * @Date 2022/8/10 15:56
 * @Version 1.0
 */
@Log
@Service
public class UserStoreServiceImpl implements UserStoreService {

    @Resource
    private BiUserStoreService biUserStoreService;
    @Resource
    private BiUserBusinessService biUserBusinessService;

    @Resource
    private BiStoreOrderMapper biStoreOrderMapper;

    @Resource
    private BiOperatedDataService biOperatedDataService;
    @Resource
    private BiCouponWriteService biCouponWriteService;

    @Resource
    private BiPoiDataMapper biPoiDataMapper;
    @Resource
    private BiApplicationService biApplicationService;
    @Resource
    private BiIndexDataMapper biIndexDataMapper;
    @Resource
    private BiIndexDataService biIndexDataService;
    @Resource
    private BiCouponWriteMapper biCouponWriteMapper;
    @Resource
    private RegressionAnalysisService regressionAnalysisService;

    @Override
    public List<BiUserStoreVo> list(QueryParam queryParam) {
        SysLoginUser sysLoginUser = LoginContextHolder.me().getSysLoginUser();
        BiUserStoreParam param = new BiUserStoreParam();
        BeanUtils.copyProperties(queryParam,param);
        param.setUserId(sysLoginUser.getId());
        List<BiUserStore> list = biUserStoreService.list(param);
        List<BiUserStoreVo> volist = list.stream().map(o ->{
            BiUserStoreVo vo = new BiUserStoreVo();
            BeanUtils.copyProperties(o,vo);
            BiUserBusiness business = biUserBusinessService.findLastRecord(o.getId(),sysLoginUser.getId());
            vo.setLastTradeTime(business.getLastTradeTime());
            return vo;
        }).collect(Collectors.toList());
        return volist;
    }

    @Override
    public StoreBusiVo storeBusiList(QueryParam queryParam) {
        StoreBusiVo vo = new StoreBusiVo();
        SysLoginUser sysLoginUser = LoginContextHolder.me().getSysLoginUser();
        List<BusinessInfo> businessInfoList = biUserBusinessService.storeBusiList(queryParam.getId(), sysLoginUser.getId());
        vo.setBusinessInfoList(businessInfoList);
        return vo;
    }

    /**
     * 订单信息汇总
     * 总订单量、总销售量、近七天交易金额趋势图、支付方式占比、退款比例
     * @param queryParam
     * @return
     */
    @Override
    @DataSource(name=WdAnalysisConst.CLICKHOUSE_DATASOURCE)
    public Object storeOrderInfo(QueryParam queryParam) {
        if (StringUtils.isEmpty(queryParam.getStoreCode())) {
            throw new ServiceException(9, "门店编号不能为空");
        }
        JSONObject result = new JSONObject();

        LambdaQueryWrapper<BiStoreOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BiStoreOrder::getStoreCode, queryParam.getStoreCode());
        Integer total = biStoreOrderMapper.selectCount(wrapper);
        result.put("total", total);

        if(total == 0){
            result.put("sumTradeAmount", 0);
            result.put("paidWayMaps", new JSONArray());
        }else{
            //总销售量
            BiStoreOrderParam orderParam = new BiStoreOrderParam();
            orderParam.setStoreCode(queryParam.getStoreCode());
            BigDecimal sumTradeAmount = biStoreOrderMapper.sumTradeAmount(orderParam);
            result.put("sumTradeAmount", sumTradeAmount);
            //支付方式占比、
            List<Map<String, Long>> paidWayMaps = biStoreOrderMapper.groupByPaidWay(orderParam);
            result.put("paidWayMaps", paidWayMaps);
            //退款比例
            LambdaQueryWrapper<BiStoreOrder> wrapper2 = new LambdaQueryWrapper<>();
            wrapper2.eq(BiStoreOrder::getStoreCode, queryParam.getStoreCode());
            wrapper2.gt(BiStoreOrder::getRefundAmount, 0);
            Integer refundCount = biStoreOrderMapper.selectCount(wrapper2);
            result.put("successRate", refundCount);
            result.put("refundRate", total - refundCount);
        }

        //近七天交易金额趋势图
        DateTime today = DateUtil.parseDate(DateUtil.today());
        List<String> xDate = new ArrayList<>();
        List<BigDecimal> yValue = new ArrayList<>();
        BigDecimal maxValue = BigDecimal.ZERO;
        for (int i = 6; i >= 0; i--) {
            DateTime dateTime = DateUtil.offsetDay(today, 0 - i);
            BiStoreOrderParam orderParam = new BiStoreOrderParam();
            orderParam.setStoreCode(queryParam.getStoreCode());
            orderParam.setCreateDate(dateTime);
            BigDecimal val = biStoreOrderMapper.sumTradeAmount(orderParam);
            xDate.add(DateUtil.formatDate(dateTime));
            yValue.add(val);
            if(val.compareTo(maxValue)>0){
                maxValue = val;
            }
        }
        JSONObject daysAmountMap = new JSONObject();
        daysAmountMap.put("xDate", xDate);
        daysAmountMap.put("yValue", yValue);
        daysAmountMap.put("maxValue", maxValue.setScale(-2,BigDecimal.ROUND_UP).intValue());
        result.put("daysAmountMap", daysAmountMap);
        return result;
    }

    @Override
    @DataSource(name=WdAnalysisConst.CLICKHOUSE_DATASOURCE)
    public PageResult<BiStoreOrder> storeOrderList(QueryParam queryParam) {
        LambdaQueryWrapper<BiStoreOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BiStoreOrder::getStoreCode, queryParam.getStoreCode());
        if (queryParam.isRefund()) {
            wrapper.gt(BiStoreOrder::getRefundAmount, BigDecimal.ZERO);
        }
        if(StringUtils.hasText(queryParam.getPaidWay())){
            wrapper.eq(BiStoreOrder::getPaidWay, queryParam.getPaidWay());
        }
        if (queryParam.getTradeAmountStart() != null) {
            wrapper.ge(BiStoreOrder::getTradeAmount,queryParam.getTradeAmountStart());
        }
        if (queryParam.getTradeAmountEnd() != null) {
            wrapper.le(BiStoreOrder::getTradeAmount,queryParam.getTradeAmountEnd());
        }
        if (queryParam.getCreateDateStart() != null) {
            wrapper.ge(BiStoreOrder::getCreateDate,queryParam.getCreateDateStart());
        }
        if (queryParam.getCreateDateEnd() != null) {
            wrapper.le(BiStoreOrder::getCreateDate,queryParam.getCreateDateEnd());
        }
        Page<BiStoreOrder> biStoreOrderPage = biStoreOrderMapper.selectPage(PageFactory.defaultPage(), wrapper);
        return new PageResult<>(biStoreOrderPage);
    }

    /**
     * 总店量、总消费券量、总订单量、总销售量
     * 商铺销量排行（Top10）、商铺销量排行（Top10/万元）
     */
    @Override
    public Object storeSalesRank(QueryParam queryParam) {
        JSONObject result = new JSONObject();
//        if (StringUtils.isEmpty(queryParam.getOrgCode())) {
//            throw new ServiceException(9, "请选择商铺OrgCode字段");
//        }
        if (StringUtils.isEmpty(queryParam.getProvName())
            &&StringUtils.isEmpty(queryParam.getCityName())) {
            queryParam.setCityName("武汉市");
        }
        //总店量
        LambdaQueryWrapper<BiOperatedData> wrap = new LambdaQueryWrapper();
        //订单查询
        LambdaQueryWrapper<BiStoreOrder> wrapper = new LambdaQueryWrapper();
        if (StringUtils.hasText(queryParam.getProvName())) {
            wrap.eq(BiOperatedData::getProvName, queryParam.getProvName());
            wrapper.eq(BiStoreOrder::getProvName,queryParam.getProvName());
        }
        if (StringUtils.hasText(queryParam.getCityName())) {
            wrap.eq(BiOperatedData::getCityName, queryParam.getCityName());
            wrapper.eq(BiStoreOrder::getCityName,queryParam.getCityName());
        }
        if (StringUtils.hasText(queryParam.getCountryName())) {
            wrap.eq(BiOperatedData::getCountryName, queryParam.getCountryName());
            wrapper.eq(BiStoreOrder::getCountryName,queryParam.getCountryName());
        }
        if (StringUtils.hasText(queryParam.getOrgCode())) {
            wrap.eq(BiOperatedData::getOrgCode, queryParam.getOrgCode());
            wrapper.eq(BiStoreOrder::getOrgCode,queryParam.getOrgCode());
        }
        boolean customAreaFlag = false;
        String lng1 = queryParam.getLng1();
        String lng2 = queryParam.getLng2();
        String lat1 = queryParam.getLat1();
        String lat2 = queryParam.getLat2();
        if (lng1 != null && lat1 != null) {
            if(lng1.compareTo(lng2)>0){
                queryParam.setLng1(lng2);
                queryParam.setLng2(lng1);
            }
            if(lat1.compareTo(lat2)>0){
                queryParam.setLat1(lat2);
                queryParam.setLat2(lat1);
            }
            customAreaFlag = true;
            wrap.between(BiOperatedData::getLat, queryParam.getLat1(),queryParam.getLat2());
            wrap.between(BiOperatedData::getLng, queryParam.getLng1(),queryParam.getLng2());
        }

        List<BiOperatedData> totalStore = biOperatedDataService.list(wrap);
        if (CollectionUtil.isEmpty(totalStore)) {
            return result;
        }
        List<String> totalStoreIds = totalStore.stream().map(el -> el.getStoreId()).collect(Collectors.toList());
        result.put("totalStore", totalStore.size());

        CurrentDataSourceContext.setDataSourceType(WdAnalysisConst.CLICKHOUSE_DATASOURCE);
        DateTime start=null,end = null;
        if (StringUtils.hasText(queryParam.getYear())) {
            start = DateUtil.parseDateTime(queryParam.getYear() + "-01-01 00:00:00");
            end = DateUtil.parseDateTime(queryParam.getYear() + "-12-31 23:59:59");
        }
        //总消费券量
        int couponCount = 0;
        if(CollectionUtil.isNotEmpty(totalStoreIds)){
            LambdaQueryWrapper<BiCouponWrite> couponWriteWrapper = new LambdaQueryWrapper<>();
            couponWriteWrapper.in(BiCouponWrite::getWriteStoreId,totalStoreIds);
            if (StringUtils.hasText(queryParam.getYear())) {
                couponWriteWrapper.between(BiCouponWrite::getOrderTime, start, end);
            }
            couponCount = biCouponWriteService.count(couponWriteWrapper);
        }
        result.put("couponCount", couponCount);
        //总订单量
        if(customAreaFlag){
            wrapper.in(BiStoreOrder::getStoreCode, totalStoreIds);
        }
        if (StringUtils.hasText(queryParam.getYear())) {
            wrapper.between(BiStoreOrder::getCreateDate, start, end);
        }
        Integer totalOrder = biStoreOrderMapper.selectCount(wrapper);
        result.put("totalOrder", totalOrder);
        //总销售量
        BiStoreOrderParam orderParam = new BiStoreOrderParam();
        BeanUtils.copyProperties(queryParam,orderParam);
        if(customAreaFlag){
            orderParam.setStoreCodeList(totalStoreIds);
        }
        BigDecimal sumTradeAmount = biStoreOrderMapper.sumTradeAmount(orderParam);
        result.put("sumTradeAmount", sumTradeAmount);

        //商铺销量排行（Top10） 订单量
        List<BiStoreOrder> countList = biStoreOrderMapper.countStoreNumByName(orderParam);
        List<BiStoreOrder>  rankList = countList.stream().sorted((e1,e2)->{
            return e2.getTradeAmount().compareTo(e1.getTradeAmount());
        }).limit(10).collect(Collectors.toList());
        result.put("rankList", rankList);

        //商铺销量排行（Top10 万元）
        List<BiStoreOrder> biStoreOrders = biStoreOrderMapper.groupTradeAmountByStore(orderParam);
        List<JSONObject>  rankList2 = biStoreOrders.stream().sorted((e1,e2)->{
            return e2.getTradeAmount().compareTo(e1.getTradeAmount());
        }).limit(10).map(el ->{
            JSONObject obj = new JSONObject();
            obj.put("storeName", el.getStoreName());
            BigDecimal divide = el.getTradeAmount().divide(new BigDecimal(10000));
            obj.put("tradeAmount", divide.setScale(2, BigDecimal.ROUND_HALF_UP));
            return obj;
        }).collect(Collectors.toList());
        result.put("rankList2", rankList2);

        //优惠券发放量排行Top10 couponRank
        List<BiStoreOrder> couponGroupList = null;
//        if(customAreaFlag){
//
//        }else{
//            couponGroupList = biStoreOrderMapper.countCouponNumByStore(null, queryParam.getYear());
//        }
        couponGroupList = biStoreOrderMapper.countCouponNumByStore(totalStoreIds, queryParam.getYear());

        List<JSONObject> couponRankList = couponGroupList.stream().sorted((e1, e2) -> {
            return e2.getTradeAmount().compareTo(e1.getTradeAmount());
        }).limit(10).map(el -> {
            JSONObject obj = new JSONObject();
            Optional<BiOperatedData> storeData = totalStore.stream().filter(el2 -> el2.getStoreId().equals(el.getStoreCode())).findFirst();
            if(storeData.isPresent()){
                obj.put("storeName", storeData.get().getStoreName());
            }else{
                obj.put("storeName", el.getStoreName());
            }
            obj.put("tradeAmount", el.getTradeAmount());
            return obj;
        }).collect(Collectors.toList());
        result.put("couponRank", couponRankList);

        CurrentDataSourceContext.clearDataSourceType();
        return result;
    }


    /**
     * 单个商铺：
     * 总消费券量、总订单量、总销售量
     * 交易金额趋势图、支付方式占比、退款比例
     * @param queryParam
     * @return
     */
    @Override
    public Object storeInformation(QueryParam queryParam) {
        String storeCode = queryParam.getStoreCode();
        if (StringUtils.isEmpty(storeCode)) {
            throw new ServiceException(9, "门店编号不能为空");
        }
        JSONObject result = new JSONObject();

        CurrentDataSourceContext.setDataSourceType(WdAnalysisConst.CLICKHOUSE_DATASOURCE);
        //总消费券量
        LambdaQueryWrapper<BiCouponWrite> couponWriteWrapper = new LambdaQueryWrapper<>();
        couponWriteWrapper.eq(BiCouponWrite::getWriteStoreId,storeCode);
        DateTime start=null,end = null;
        if (StringUtils.hasText(queryParam.getYear())) {
            start = DateUtil.parseDateTime(queryParam.getYear() + "-01-01 00:00:00");
            end = DateUtil.parseDateTime(queryParam.getYear() + "-12-31 23:59:59");
            couponWriteWrapper.between(BiCouponWrite::getOrderTime, start, end);
        }
        int couponCount = biCouponWriteService.count(couponWriteWrapper);
        result.put("couponCount", couponCount);
        //总订单量
        LambdaQueryWrapper<BiStoreOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BiStoreOrder::getStoreCode, storeCode);
        if (StringUtils.hasText(queryParam.getYear())) {
            wrapper.between(BiStoreOrder::getCreateDate, start, end);
        }
        Integer totalOrder = biStoreOrderMapper.selectCount(wrapper);
        result.put("totalOrder", totalOrder);
        //总销售量
        BiStoreOrderParam orderParam = new BiStoreOrderParam();
        BeanUtils.copyProperties(queryParam,orderParam);
        BigDecimal sumTradeAmount = biStoreOrderMapper.sumTradeAmount(orderParam);
        result.put("sumTradeAmount", sumTradeAmount);
        // 交易金额趋势图
        result.put("tradeAmountTrend",tradeAmountTrend(queryParam));
        //支付方式占比、
        List<Map<String, Long>> paidWayMaps = biStoreOrderMapper.groupByPaidWay(orderParam);
        result.put("paidWayMaps", paidWayMaps);
        //退款比例
        LambdaQueryWrapper<BiStoreOrder> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(BiStoreOrder::getStoreCode, queryParam.getStoreCode());
        wrapper2.gt(BiStoreOrder::getRefundAmount, 0);
        Integer refundCount = biStoreOrderMapper.selectCount(wrapper2);
        result.put("successRate", totalOrder - refundCount);
        result.put("refundRate", refundCount);
        CurrentDataSourceContext.clearDataSourceType();

        List<BiStoreOrder> biStoreOrders = queryOrderListForTrend(queryParam);
        //客流量分析统计
        result.put("passengerFlowTrend",passengerFlowTrend(queryParam,biStoreOrders));
        //客单价分析统计
        result.put("passengerPriceTrend",passengerPriceTrend(queryParam,biStoreOrders));
        //优惠券的核销数量统计
        result.put("couponUsedTrend",couponUsedTrend(queryParam));
        //销售额影响因素
        StoreQueryParam storeQueryParam = new StoreQueryParam();
        storeQueryParam.setStoreCode(storeCode);
        result.put("tradeEffectFactor", regressionAnalysisService.calcTradeEffectFactor(storeQueryParam));
        //优惠券影响因素分析
        result.put("couponAnalysisFactor", regressionAnalysisService.couponAnalysisFactor(storeQueryParam));


        return result;
    }

    /**
     * 交易金额趋势图
     * @param queryParam
     * @return
     */
    private JSONObject tradeAmountTrend(QueryParam queryParam){
        JSONObject tradeAmountTrend = new JSONObject();
        //交易金额趋势图 1月-12月 交易金额汇总
        String storeCode = queryParam.getStoreCode();
        if (StringUtils.hasText(queryParam.getYear())) {
            List<String> xDateList = new ArrayList<>();
            List<BigDecimal> yValueList = new ArrayList<>();
            BigDecimal maxValue = BigDecimal.ZERO;
            for (int i = 1; i <= 12; i++) {
                String mm = i < 10 ? "0" + i : String.valueOf(i);
                Date monthStart = DateUtil.parseDateTime(queryParam.getYear() + "-"+mm+"-01 00:00:00");
                BigDecimal bigDecimal = biStoreOrderMapper.sumStoreTradeAmountByMonth(storeCode, monthStart);
                if(bigDecimal.compareTo(maxValue)>0){
                    maxValue = bigDecimal;
                }
                xDateList.add(queryParam.getYear() + "-"+mm);
                yValueList.add(bigDecimal);
            }
            tradeAmountTrend.put("xDate", xDateList);
            tradeAmountTrend.put("yValue", yValueList);
            tradeAmountTrend.put("maxValue", maxValue.setScale(-5,BigDecimal.ROUND_UP).intValue());
            //使用回归分析预测没有数据的月份
            tradeAmountTrend.put("list", predictTradeAmountTrend(xDateList,yValueList));
        }
        return tradeAmountTrend;
    }

    private JSONArray predictTradeAmountTrend(List<String> xDateList,List<BigDecimal> yValueList) {
        JSONArray result = new JSONArray();
        List<BigDecimal> newList = new ArrayList<>();
        for (BigDecimal b : yValueList) {
            if(b.compareTo(BigDecimal.ZERO)>0){
                newList.add(b);
            }
        }
        if (newList.size() > 1) {
            int sampleSize = 12;
            double[][] xValue = new double[sampleSize][];
            double[] yValue = new double[sampleSize];
            for (int i = 0; i < sampleSize; i++) {
                xValue[i] = new double[]{i};
                if (i >= newList.size()) {
                    yValue[i]=newList.get(i%newList.size()).doubleValue();
                }else{
                    yValue[i]=newList.get(i).doubleValue();
                }

            }
            //创建多元线性回归模型
            OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
            regression.newSampleData(yValue, xValue);
            double[] estimates = regression.estimateRegressionParameters();
            for (int i = 0; i < yValueList.size(); i++) {
                String name = xDateList.get(i);
                BigDecimal b = yValueList.get(i);
                JSONObject element = new JSONObject();
                element.put("name", name);
                if(b.compareTo(BigDecimal.ZERO)>0){
                    element.put("value", b);
                }else{
                    int predictValue = new BigDecimal(i * estimates[1] + estimates[0]).setScale(0, 4).intValue();
                    if (predictValue < 0) {
                        predictValue = 0;
                    }
                    element.put("value",predictValue);
                    element.put("predict", true);
                }
                result.add(element);
            }
        }
        return result;
    }

    /**
     * 查询clickhouse数据库，返回订单信息列表
     * @param param
     * @return
     */
    private List<BiStoreOrder>  queryOrderListForTrend(QueryParam param){
        if (StringUtils.isEmpty(param.getEndDate())) {
            param.setEndDate(param.getStartDate());
        }
        String startDate = param.getStartDate();
        String endDate = param.getEndDate();
        String storeCode = param.getStoreCode();
        if(StringUtils.isEmpty(storeCode)||StringUtils.isEmpty(startDate)){
            throw new ServiceException(9,"店铺编号、开始时间不能为空");
        }
        if(startDate.length()==4){
            param.setDateFormat("年");
        }else if(startDate.length()==6){
            param.setDateFormat("月");
        }else if(startDate.length()==8){
            param.setDateFormat("日");
        }else if(startDate.length()==10){
            param.setDateFormat("时");
        }else{
            throw new ServiceException(9,"时间格式不正确:"+startDate);
        }
        String dateFormat = param.getDateFormat();

        Date date1 = null,date2 = null;
        if (dateFormat.equals("年")) {
            date1 = DateUtil.parse(startDate, "yyyy");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyy"), DateField.YEAR, 1);
        }else if (dateFormat.equals("月")) {
            date1 = DateUtil.parse(startDate, "yyyyMM");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyyMM"), DateField.MONTH, 1);
        }else if (dateFormat.equals("日")) {
            date1 = DateUtil.parse(startDate, "yyyyMMdd");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyyMMdd"), DateField.DAY_OF_MONTH, 1);
        }else if (dateFormat.equals("时")) {
            date1 = DateUtil.parse(startDate, "yyyyMMddHH");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyyMMddHH"), DateField.HOUR_OF_DAY, 1);
        }
        //查询数据库
        LambdaQueryWrapper<BiStoreOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BiStoreOrder::getStoreCode, storeCode);
        wrapper.between(BiStoreOrder::getCreateDate, date1, date2);
        wrapper.gt(BiStoreOrder::getTradeAmount, 0);
        CurrentDataSourceContext.setDataSourceType(WdAnalysisConst.CLICKHOUSE_DATASOURCE);
        List<BiStoreOrder> biStoreOrders = biStoreOrderMapper.selectList(wrapper);
        CurrentDataSourceContext.clearDataSourceType();
        return biStoreOrders;
    }

    /**
     * 查询clickhouse数据库，返回消费券信息列表
     * @param param
     * @return
     */
    private List<BiCouponWrite>  queryCouponListForTrend(QueryParam param){
        if (StringUtils.isEmpty(param.getEndDate())) {
            param.setEndDate(param.getStartDate());
        }
        String startDate = param.getStartDate();
        String endDate = param.getEndDate();
        String storeCode = param.getStoreCode();

        if(StringUtils.isEmpty(storeCode)||StringUtils.isEmpty(startDate)){
            throw new ServiceException(9,"店铺编号、开始时间不能为空");
        }
        if(startDate.length()==4){
            param.setDateFormat("年");
        }else if(startDate.length()==6){
            param.setDateFormat("月");
        }else if(startDate.length()==8){
            param.setDateFormat("日");
        }else if(startDate.length()==10){
            param.setDateFormat("时");
        }else{
            throw new ServiceException(9,"时间格式不正确:"+startDate);
        }
        String dateFormat = param.getDateFormat();

        Date date1 = null,date2 = null;
        if (dateFormat.equals("年")) {
            date1 = DateUtil.parse(startDate, "yyyy");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyy"), DateField.YEAR, 1);
        }else if (dateFormat.equals("月")) {
            date1 = DateUtil.parse(startDate, "yyyyMM");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyyMM"), DateField.MONTH, 1);
        }else if (dateFormat.equals("日")) {
            date1 = DateUtil.parse(startDate, "yyyyMMdd");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyyMMdd"), DateField.DAY_OF_MONTH, 1);
        }else if (dateFormat.equals("时")) {
            date1 = DateUtil.parse(startDate, "yyyyMMddHH");
            date2 = DateUtil.offset(DateUtil.parse(endDate, "yyyyMMddHH"), DateField.HOUR_OF_DAY, 1);
        }

        CurrentDataSourceContext.setDataSourceType(WdAnalysisConst.CLICKHOUSE_DATASOURCE);
        //总消费券量
        LambdaQueryWrapper<BiCouponWrite> couponWriteWrapper = new LambdaQueryWrapper<>();
        couponWriteWrapper.eq(BiCouponWrite::getWriteStoreId,storeCode);
        couponWriteWrapper.between(BiCouponWrite::getOrderTime, date1, date2);
        List<BiCouponWrite> list = biCouponWriteService.list(couponWriteWrapper);
        CurrentDataSourceContext.clearDataSourceType();
        return list;
    }
    /**
     * 客流量分析统计
     * "1）流量分析：统计每天在对应门店里面的下单人数。
     * 2）可以根据时间的演变进行数据的筛选（时间的筛选）。
     * a．如果时间筛选纬度为“天”，则趋势图以“小时”为单位
     * b．如果时间筛选纬度为“月”，则趋势图以“天”为单位
     * c．如果时间筛选纬度为“年”，则趋势图以“月”为单位"
     * @return
     */
    private JSONObject passengerFlowTrend(QueryParam param,List<BiStoreOrder> biStoreOrders){
        JSONObject result = new JSONObject();
        if (CollectionUtil.isEmpty(biStoreOrders)) {
            return result;
        }
        String dateFormat = param.getDateFormat();
        //按照条件分组
        HashMap<String, Integer> groupMap = new HashMap();
        for (BiStoreOrder o : biStoreOrders) {
            String key = getGroupMapKeyByDate(dateFormat, o.getCreateDate());
            Integer count = groupMap.get(key);
            if (count == null) {
                count = 1;
            }else{
                count ++;
            }
            groupMap.put(key, count);
        }
        List<Integer> yValueList = new ArrayList<>();

        ArrayList<JSONObject> yValue = new ArrayList();
        groupMap.keySet().stream().sorted().forEach(str->{
            JSONObject element = new JSONObject();
            element.put("key",str);
            element.put("name",getDateLabelString(str));
            element.put("value",groupMap.get(str));
            yValue.add(element);
        });
        //使用一元线性回归 预测未来6条数据
        addPredictValue(yValue);
        JSONArray array = new JSONArray();
        array.add(yValue);
        result.put("yValue", array);
        result.put("maxValue", yValue.stream().mapToInt(e->e.getInteger("value")).max().getAsInt());

        return result;
    }

    /**
     * 客单价分析统计
     * 1）客单价分析：根据门店内客单价的最底单价及最高单价进行5个等级，分析计算并展示出每个等级的占比及数量。
     * 2）可以根据时间的演变进行数据的筛选（时间的筛选）。
     * a．如果时间筛选纬度为“天”，则趋势图以“小时”为单位
     * b．如果时间筛选纬度为“月”，则趋势图以“天”为单位
     * c．如果时间筛选纬度为“年”，则趋势图以“月”为单位
     * @param param
     * @return
     */
    private JSONObject passengerPriceTrend(QueryParam param,List<BiStoreOrder> biStoreOrders){
        JSONObject result = new JSONObject();
        if (CollectionUtil.isEmpty(biStoreOrders)) {
            return result;
        }
        String dateFormat = param.getDateFormat();
        //获取订单金额的最大值和最小值，按照条件分成5组
        double max = biStoreOrders.stream().mapToDouble(e -> e.getTradeAmount().doubleValue()).max().getAsDouble();
        double min = biStoreOrders.stream().mapToDouble(e -> e.getTradeAmount().doubleValue()).min().getAsDouble();
        double p = new BigDecimal((max - min) / 4).setScale(2, 4).doubleValue();
        ArrayList<String> priceLabelList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            String t1 = new BigDecimal(min+(p*i)).setScale(2,4).toPlainString();
            String t2 = new BigDecimal(min+(p*(i+1))).setScale(2,4).toPlainString();
            priceLabelList.add(t1 + "~" + t2);
        }

        HashMap<String, HashMap<String,Integer>> groupMap = new HashMap();
        for (BiStoreOrder o : biStoreOrders) {
            String key = getGroupMapKeyByDate(dateFormat, o.getCreateDate());
            HashMap<String, Integer> hashMap = groupMap.get(key);
            if (hashMap == null) {
                hashMap = new HashMap<>();
            }
            int i = o.getTradeAmount().subtract(new BigDecimal(min)).divide(new BigDecimal(p), 0, 4).intValue();
            if(i<0) i = 0;
            if(i>4) i = 4;

            Integer count = hashMap.get(priceLabelList.get(i));
            if (count == null) {
                count = 1;
            }else{
                count ++;
            }
            hashMap.put(priceLabelList.get(i), count);
            groupMap.put(key, hashMap);
        }

        //单价等级-List<value>
        HashMap<String, List<JSONObject>> yDataMap = new HashMap<>();
        // 日期-[单价等级：数量]
        groupMap.keySet().stream().sorted().forEach(str ->{
            HashMap<String, Integer> hashMap = groupMap.get(str);

            Set<String> levelSet = hashMap.keySet();
            for (String level : levelSet) {
                List<JSONObject> subList = yDataMap.get(level);
                if (subList == null) {
                    subList = new ArrayList<>();
                }
                Integer val = hashMap.get(level);
                if(val == null){
                    val = 0;
                }
                JSONObject element = new JSONObject();
                element.put("key", str);
                element.put("name",getDateLabelString(str));
                element.put("value", val);
                subList.add(element);
                yDataMap.put(level, subList);
            }
        });

        Collection<List<JSONObject>> values = yDataMap.values();
        for (List<JSONObject> subList : values) {
            //使用一元线性回归 预测未来6条数据
            this.addPredictValue(subList);
        }

        HashSet<String> nameSet = new HashSet();
        yDataMap.values().stream().flatMap(el ->el.stream()).forEach(e->nameSet.add(e.getString("name")));
        List<String> nameList = nameSet.stream().sorted().collect(Collectors.toList());
        List<List<JSONObject>> arrayNew = new ArrayList<>();
        for(String level : priceLabelList){
            List<JSONObject> subList = yDataMap.get(level);
            if(subList == null){
                subList = new ArrayList<>();
            }

            HashMap<String,JSONObject> subMap = new HashMap();
            for (JSONObject obj : subList) {
                subMap.put(obj.getString("name"), obj);
            }
            List<JSONObject> subListNew = new ArrayList<>();
            for (String name : nameList) {
                JSONObject element = subMap.get(name);
                if (element == null) {
                    element = new JSONObject();
                    element.put("name", name);
                    element.put("value", 0);
                }
                subListNew.add(element);
            }
            arrayNew.add(subListNew);
        }

        int maxValue = yDataMap.values().stream().flatMap(el ->el.stream()).mapToInt(el->el.getInteger("value")).max().getAsInt();
        result.put("maxValue", maxValue);


        result.put("yValue", arrayNew);
        result.put("priceLabelList", priceLabelList);
        return result;
    }

    /**
     * 统计优惠券的核销数量
     *以核销时间为纬度进行趋势统计（折线图）
     * 2）可以根据优惠券类型进行数据的筛选（优惠券类型筛选）
     * 3）可以根据时间的演变进行数据的筛选（时间的筛选）。
     * a．如果时间筛选纬度为“天”，则趋势图以“小时”为单位
     * b．如果时间筛选纬度为“月”，则趋势图以“天”为单位
     * c．如果时间筛选纬度为“年”，则趋势图以“月”为单位"
     * @param param
     * @return
     */
    private JSONObject couponUsedTrend(QueryParam param){
        JSONObject result = new JSONObject();
        List<BiCouponWrite> couponWriteList = this.queryCouponListForTrend(param);
        if (CollectionUtil.isEmpty(couponWriteList)) {
            return result;
        }
        String dateFormat = param.getDateFormat();

        //按照条件分组
        HashMap<String, Integer> groupMap = new HashMap();
        for (BiCouponWrite o : couponWriteList) {
            DateTime dateTime = DateUtil.parseDate(o.getOrderTime());
            String key = getGroupMapKeyByDate(dateFormat,dateTime);
            Integer count = groupMap.get(key);
            if (count == null) {
                count = 1;
            }else{
                count ++;
            }
            groupMap.put(key, count);
        }

        ArrayList<JSONObject> yValue = new ArrayList();
        groupMap.keySet().stream().sorted().forEach(str->{
            JSONObject element = new JSONObject();
            element.put("key",str);
            element.put("name",getDateLabelString(str));
            element.put("value",groupMap.get(str));
            yValue.add(element);
        });
        //使用一元线性回归 预测未来6条数据
        addPredictValue(yValue);
        JSONArray array = new JSONArray();
        array.add(yValue);
        result.put("yValue", array);
        result.put("maxValue", yValue.stream().mapToInt(e->e.getInteger("value")).max().getAsInt());

        return result;
    }

    private String getGroupMapKeyByDate(String dateFormat, Date date) {
        String key = null;
        if (dateFormat.equals("年")) {
            key = DateUtil.format(date,"yyyy");
        }else if (dateFormat.equals("月")) {
            key = DateUtil.format(date,"yyyyMM");
        }else if (dateFormat.equals("日")) {
            key = DateUtil.format(date,"yyyyMMdd");
        }else if (dateFormat.equals("时")) {
            key = DateUtil.format(date,"yyyyMMddHH");
        }
        return key;
    }
    private String getDateLabelString(String str){
        String name;
        if(str.length()==4){
            name = str.substring(0, 4)+"年";
        }else if(str.length()==6){
            name = str.substring(0, 4) + "年" + str.substring(4) + "月";
        }else if(str.length()==8){
            name = str.substring(4, 6)+"月"+str.substring(6)+"日";
        }else{
            name = str.substring(6, 8)+"日"+str.substring(8)+"时";
        }
        return name;
    }

    /**
     * 使用回归分析预测yValueList后面6位值
     * @return
     */
    public void addPredictValue(List<JSONObject> yValue){
        int yValueLength = yValue.size();
        double[] yList = yValue.stream().mapToDouble(e -> e.getDouble("value")).toArray();
        double[][] xList = new double[yValueLength][];
        for (int i=0;i<yValueLength;i++) {
            xList[i] = new double[]{i};
        }
        if(xList.length < 2){
            return;
        }
        JSONObject last = yValue.get(yValue.size()-1);
        String key = last.getString("key");
        String formatStr = null;
        String dateFormat = null;
        DateField dateField = null;
        if(key.length()==4){
            formatStr = "yyyy年";
            dateFormat = "yyyy";
            dateField = DateField.YEAR;
        }else if(key.length()==6){
            formatStr = "yyyy年MM月";
            dateFormat = "yyyyMM";
            dateField = DateField.MONTH;
        }else if(key.length()==8){
            formatStr = "MM月dd日";
            dateFormat = "yyyyMMdd";
            dateField = DateField.DAY_OF_MONTH;
        }else{
            formatStr = "dd日HH时";
            dateFormat = "yyyyMMddHH";
            dateField = DateField.HOUR_OF_DAY;
        }
        Date date = DateUtil.parse(key, dateFormat);
        //创建多元线性回归模型
        OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
        regression.newSampleData(yList, xList);
        double[] estimates = regression.estimateRegressionParameters();
        ArrayList<String> xDateList = new ArrayList<>();
        List<Integer> predictValueList = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            DateTime offset = DateUtil.offset(date, dateField, i+1);
            int predictValue = 0;
            if(i < 1){
                //第1个值预测结果延续趋势
                int a = yValue.get(yValue.size()-1).getInteger("value");
                int b = yValue.get(yValue.size()-2).getInteger("value");
                predictValue = a + (a-b);
            }else if(i <= 2){
                // 第2,3个值中和延续趋势和回归分析
                int a = yValue.get(yValue.size()-1).getInteger("value");
                int b = yValue.get(yValue.size()-2).getInteger("value");
                int predictValueA = a + (a-b);
                int x = yValueLength + i;
                int predictValueB = new BigDecimal(x * estimates[1] + estimates[0]).setScale(0, 4).intValue();
                predictValue = (predictValueA + predictValueB)/2;
            }else{
                //第4，5，6取回归分析值
                int x = yValueLength + i;
                predictValue = new BigDecimal(x * estimates[1] + estimates[0]).setScale(0, 4).intValue();
            }
            if(predictValue<0){
                predictValue = 0;
            }

            JSONObject element = new JSONObject();
            element.put("name", DateUtil.format(offset, formatStr));
            element.put("value", predictValue);
            element.put("predict", true);
            yValue.add(element);
        }
    }

    /**
     * 多个商铺对比分析：
     * 各商铺销量分析、各商铺销售额分析、各商铺消费券分析
     * @param queryParam
     * @return
     */
    @Override
    public Object storeCompare(QueryParam queryParam) {
        List<String> storeCodeList = queryParam.getStoreCodeList();
        String year = queryParam.getYear();
        if (storeCodeList == null || storeCodeList.size()<1) {
            throw new ServiceException(9, "门店编号列表不能为空");
        }
        if (StringUtils.isEmpty(year)) {
            throw new ServiceException(9, "年份不能为空");
        }
        DateTime start = DateUtil.parseDateTime(queryParam.getYear() + "-01-01 00:00:00");
        DateTime end = DateUtil.parseDateTime(queryParam.getYear() + "-12-31 23:59:59");
        JSONObject result = new JSONObject();
        //各商铺消费券分析
        Map<String, String> storeNameMap = new HashMap<>();
        for (String storeCode : storeCodeList) {
            LambdaQueryWrapper<BiOperatedData> wrapper = new LambdaQueryWrapper();
            wrapper.select(BiOperatedData::getStoreName);
            wrapper.eq(BiOperatedData::getStoreId,storeCode);
            wrapper.last(" limit 1 ");
            String storeName = biOperatedDataService.getOne(wrapper).getStoreName();
            storeNameMap.put(storeCode,storeName);
        }

        //各商铺销量分析、各商铺销售额分析
        CurrentDataSourceContext.setDataSourceType(WdAnalysisConst.CLICKHOUSE_DATASOURCE);
        List<JSONObject> couponCountList = new ArrayList<>();
        List<JSONObject> tradeTotalList = new ArrayList<>();
        List<JSONObject> tradeAmountList = new ArrayList<>();

        for (String storeCode : storeCodeList) {
            LambdaQueryWrapper<BiCouponWrite> couponWriteWrapper = new LambdaQueryWrapper<>();
            couponWriteWrapper.eq(BiCouponWrite::getWriteStoreId,storeCode);
            couponWriteWrapper.in(BiCouponWrite::getOrderTime, start, end);
            int couponCount = biCouponWriteService.count(couponWriteWrapper);
            JSONObject object = new JSONObject();
            object.put("name",storeNameMap.get(storeCode));
            object.put("value",couponCount);
            couponCountList.add(object);
        }
        for (String storeCode : storeCodeList) {
            //总订单量
            LambdaQueryWrapper<BiStoreOrder> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(BiStoreOrder::getStoreCode, storeCode);
            if (StringUtils.hasText(queryParam.getYear())) {
                wrapper.between(BiStoreOrder::getCreateDate, start, end);
            }
            Integer totalOrder = biStoreOrderMapper.selectCount(wrapper);
            JSONObject obj1 = new JSONObject();
            obj1.put("name",storeNameMap.get(storeCode));
            obj1.put("value",totalOrder);
            tradeTotalList.add(obj1);
            //总销售量
            BiStoreOrderParam orderParam = new BiStoreOrderParam();
            orderParam.setYear(year);
            orderParam.setStoreCode(storeCode);
            BigDecimal sumTradeAmount = biStoreOrderMapper.sumTradeAmount(orderParam);
            JSONObject obj2 = new JSONObject();
            obj2.put("name",storeNameMap.get(storeCode));
            obj2.put("value",sumTradeAmount);
            tradeAmountList.add(obj2);
        }
        CurrentDataSourceContext.clearDataSourceType();
        result.put("couponCountList", couponCountList);
        result.put("couponCount", couponCountList.stream().mapToInt(e->e.getInteger("value")).sum());
        result.put("tradeTotalList", tradeTotalList);
        result.put("tradeTotal", tradeTotalList.stream().mapToInt(e->e.getInteger("value")).sum());
        result.put("tradeAmountList", tradeAmountList);
        result.put("tradeAmount", tradeAmountList.stream().mapToDouble(e->e.getBigDecimal("value").doubleValue()).sum());
        return result;
    }

    /**
     * 查询单个商铺的消费券信息
     * 优惠券总数量、优惠券总金额、近七天优惠券数量趋势图、优惠券使用率
     * @param queryParam
     * @return
     */
    @Override
    @DataSource(name=WdAnalysisConst.CLICKHOUSE_DATASOURCE)
    public Object storeCouponInfo(QueryParam queryParam) {
        String storeCode = queryParam.getStoreCode();
        if (StringUtils.isEmpty(storeCode)) {
            throw new ServiceException(9, "门店编号不能为空");
        }
        JSONObject result = new JSONObject();
        DateTime start=null,end = null;
        if (StringUtils.hasText(queryParam.getYear())) {
            start = DateUtil.parseDateTime(queryParam.getYear() + "-01-01 00:00:00");
            end = DateUtil.parseDateTime(queryParam.getYear() + "-12-31 23:59:59");
        }
        //优惠券总数量
        LambdaQueryWrapper<BiCouponWrite> couponWriteWrapper = new LambdaQueryWrapper<>();
        couponWriteWrapper.eq(BiCouponWrite::getWriteStoreId,storeCode);
        couponWriteWrapper.between(BiCouponWrite::getOrderTime, start, end);
        List<BiCouponWrite> couponWriteList = biCouponWriteService.list(couponWriteWrapper);
        result.put("couponCount",couponWriteList.size());
        //优惠券总金额
        double sum = couponWriteList.stream().mapToDouble(el -> Double.parseDouble(el.getOrderAmount().toString())).sum();
        result.put("couponSum",sum);
        //近七天优惠券数量趋势图
        DateTime today = DateUtil.parseDate(DateUtil.today());
        List<String> xDate = new ArrayList<>();
        List<Integer> yValue = new ArrayList<>();
        int maxValue = 0;
        for (int i = 6; i >= 0; i--) {
            DateTime dateTime = DateUtil.offsetDay(today, 0 - i);
            LambdaQueryWrapper<BiCouponWrite> w = new LambdaQueryWrapper<>();
            w.eq(BiCouponWrite::getWriteStoreId,storeCode);
            w.eq(BiCouponWrite::getOrderTime, dateTime);
            int count = biCouponWriteService.count(w);
            xDate.add(DateUtil.formatDate(dateTime));
            yValue.add(count);
            if(count > maxValue){
                maxValue = count;
            }
        }
        JSONObject daysAmountMap = new JSONObject();
        daysAmountMap.put("xDate", xDate);
        daysAmountMap.put("yValue", yValue);
        daysAmountMap.put("maxValue", new BigDecimal(maxValue).setScale(-2,BigDecimal.ROUND_UP).intValue());
        result.put("daysCouponMap", daysAmountMap);

        return result;
    }

    /**
     * 查询单个商铺的消费券信息
     * 消费券详情列表
     * @param queryParam
     * @return
     */
    @Override
    @DataSource(name=WdAnalysisConst.CLICKHOUSE_DATASOURCE)
    public PageResult<BiCouponWrite> storeCouponList(BiCouponWriteParam queryParam) {
        DateTime start=null,end = null;
        //优惠券总数量
        LambdaQueryWrapper<BiCouponWrite> couponWriteWrapper = new LambdaQueryWrapper<>();
        couponWriteWrapper.eq(BiCouponWrite::getWriteStoreId,queryParam.getStoreCode());
        if (StringUtils.hasText(queryParam.getYear())) {
            start = DateUtil.parseDateTime(queryParam.getYear() + "-01-01 00:00:00");
            end = DateUtil.parseDateTime(queryParam.getYear() + "-12-31 23:59:59");
            couponWriteWrapper.between(BiCouponWrite::getOrderTime, start, end);
        }

        if(StringUtils.hasText(queryParam.getActivityName())){
            couponWriteWrapper.like(BiCouponWrite::getActivityName,queryParam.getActivityName());
        }
        if(StringUtils.hasText(queryParam.getActivityType())){
            couponWriteWrapper.like(BiCouponWrite::getActivityType,queryParam.getActivityType());
        }
        Page<BiCouponWrite> page = biCouponWriteService.page(PageFactory.defaultPage(), couponWriteWrapper);
        return new PageResult<>(page);
    }

    @Override
    public Object storeSearch(QueryParam queryParam) {
        JSONObject result = new JSONObject();
        if (StringUtils.isEmpty(queryParam.getStoreName())) {
            result.put("rows", Collections.EMPTY_LIST);
            return result;
        }
        LambdaQueryWrapper<BiOperatedData> wrap = new LambdaQueryWrapper();
        wrap.like(BiOperatedData::getStoreName, queryParam.getStoreName());
        if (StringUtils.hasText(queryParam.getOrgCode())) {
            wrap.eq(BiOperatedData::getOrgCode, queryParam.getOrgCode());
        }
        if (StringUtils.isEmpty(queryParam.getProvName())
                && StringUtils.isEmpty(queryParam.getCityName())) {
            wrap.eq(BiOperatedData::getProvName, "湖北省");
        }
        if(StringUtil.isNotEmpty(queryParam.getCityName())){
            wrap.eq(BiOperatedData::getCityName, queryParam.getCityName());
        }
        wrap.last(" limit 10 ");
        List<BiOperatedData> totalStore = biOperatedDataService.list(wrap);
        if (CollectionUtil.isEmpty(totalStore)) {

        }
        List<BiApplication> apps = biApplicationService.list();
        HashMap<String, String> imageUrlMap = new HashMap<>();
        for (BiApplication app : apps) {
            String imageUrl = app.getImageUrl();
            if (StringUtils.hasText(imageUrl) && imageUrl.contains(".")) {
                String[] split = imageUrl.split("\\.");
                imageUrl = split[0]+"-active."+split[1];
            }
            imageUrlMap.put(app.getOrgCode(), imageUrl);
        }

        List<JSONObject> collect = totalStore.stream().map(el -> {
            JSONObject obj = new JSONObject();
            obj.put("name", el.getStoreName());
            obj.put("address", el.getAddress());
            obj.put("lng", el.getLng());
            obj.put("lat", el.getLat());
            obj.put("iconImage", imageUrlMap.get(el.getOrgCode()));
            return obj;
        }).collect(Collectors.toList());
        result.put("rows",collect);
        return result;
    }

    /**
     *
     //业务中心 展开茶百道时，提供“消费券列表”、“客流量”，“客单价”菜单
     //消费券列表查询clickhouse，按照消费券类型分组
     //查询某个区域的道路网密度图层获取图层网格,以及查询某个区域的茶百道的坐标
     //遍历网格判断点位是否在网格内
     //如果店铺在网格内则统计“消费券列表”或“客流量”或“客单价”
     //按照网格的形式返回数据
     * @param queryParam
     * @return
     */
    public JSONObject showStoreGrid(QueryParam queryParam){
        // orgCode ==> [chabaidao,chabaidao_coupon,chabaidao_coupon_2.00元代金券,chabaidao_passengerFlow,chabaidao_passengerPrice]
        String orgCodeString = queryParam.getOrgCode();
        List<String> orgCodeList = queryParam.getOrgCodeList();
        if (StringUtils.isEmpty(orgCodeString)) {
            if (CollectionUtil.isEmpty(orgCodeList)) {
                throw new ServiceException(9, "商品编号orgCode不能为空");
            }
            for(String s : orgCodeList){
                if(s.contains("_")){
                    orgCodeString = s;
                    break;
                }
            }
        }
        if (StringUtils.isEmpty(queryParam.getCityName()) && StringUtils.isEmpty(queryParam.getProvName())) {
            queryParam.setCityName("武汉市");
        }
        JSONObject result = new JSONObject();
        String orgCode = orgCodeString;
        String type = "";
        if (orgCodeString.contains("_")) {
            int ch = orgCodeString.indexOf('_');
            orgCode = orgCodeString.substring(0, ch);
            type = orgCodeString.substring(ch + 1);
        }
        //查询某个区域的道路网密度图层获取图层网格
        log.info("获取图层网格数据");
        LambdaQueryWrapper<BiIndexData> w = new LambdaQueryWrapper<>();
        w.select(BiIndexData::getGrid,BiIndexData::getLng, BiIndexData::getLat, BiIndexData::getTypeId, BiIndexData::getValue, BiIndexData::getMultipolygon);
        w.eq(BiIndexData::getName, "道路网密度");
        if (StringUtils.hasText(queryParam.getCityName())) {
            w.eq(BiIndexData::getCityName,queryParam.getCityName());
        }
        if (StringUtils.hasText(queryParam.getCountryName())) {
            w.eq(BiIndexData::getCountryName,queryParam.getCountryName());
        }
        CurrentDataSourceContext.setDataSourceType(WdAnalysisConst.CLICKHOUSE_DATASOURCE);
        List<BiIndexData> biIndexData = biIndexDataMapper.selectList(w);
        CurrentDataSourceContext.clearDataSourceType();
        if (CollectionUtil.isEmpty(biIndexData)) {
            return result;
        }
        log.info("获取图层网格数据结束："+biIndexData.size());
        //查询某个区域的茶百道的坐标
        log.info("获取商铺数据："+orgCode);
        LambdaQueryWrapper<BiOperatedData> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BiOperatedData::getOrgCode,orgCode);
        if(StringUtils.hasText(queryParam.getCityName())){
            wrapper.eq(BiOperatedData::getCityName,queryParam.getCityName());
        }
        if(StringUtils.hasText(queryParam.getCountryName())){
            wrapper.eq(BiOperatedData::getCountryName,queryParam.getCountryName());
        }
        List<BiOperatedData> storeList = biOperatedDataService.list(wrapper);
        if (CollectionUtil.isEmpty(storeList)) {
            return result;
        }
        String name = null;
        log.info("获取商铺数据结束："+storeList.size());
        CurrentDataSourceContext.setDataSourceType(WdAnalysisConst.CLICKHOUSE_DATASOURCE);
        HashMap<String, Double> valueMap = new HashMap<>();
        if(type.startsWith("coupon")){
            //优惠券核销量
            List<String> couponNameList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(orgCodeList)) {
                for (String s : orgCodeList) {
                    if (s.contains("coupon_")) {
                        couponNameList.add(s.split("_")[2]);
                    }
                }
            }
            List<Map<String, Object>> maps = biCouponWriteMapper.countNumGroupByStoreId(orgCode,couponNameList);
            for (Map<String, Object> map : maps) {
                String k = String.valueOf(map.get("write_store_id"));
                double v = Double.parseDouble(String.valueOf(map.get("total")));
                valueMap.put(k,v);
            }
            name = "优惠券核销量";
            log.info("获取优惠券核销量："+maps.size());
        }else if(type.equals("passengerFlow")){
            //客流量
            BiStoreOrderParam p = new BiStoreOrderParam();
            p.setOrgCode(orgCode);
            List<BiStoreOrder> biStoreOrders = biStoreOrderMapper.countStoreNumByName(p);
            for (BiStoreOrder o : biStoreOrders) {
                log.info(JSONObject.toJSONString(o));
                valueMap.put(o.getStoreName(),o.getTradeAmount().doubleValue());
            }
            name = "客流量";
            log.info("获取客流量数据："+biStoreOrders.size());
        }else if(type.equals("passengerPrice")){
            //客单价 平均值
            List<Map<String, Object>> maps = biStoreOrderMapper.avgTradeAmountByOrgCode(orgCode);
            for (Map<String, Object> map : maps) {
                String k = String.valueOf(map.get("store_name"));
                double v = Double.parseDouble(String.valueOf(map.get("average")));
                valueMap.put(k,v);
            }
            name = "客单价";
            log.info("获取客单价数据："+maps.size());
        }else{
            log.info("其他类型不处理："+type);
            return result;
        }
        if(valueMap.size()==0){
            log.info("valueMap为空："+type);
            return result;
        }
        log.info("valueMap size:"+valueMap.size());
        CurrentDataSourceContext.clearDataSourceType();
        //遍历网格判断点位是否在网格内
        JSONArray pointArray = new JSONArray();
        List<String> colorLevel = GridColorUtils.getColorLevel(null);
        double max = valueMap.values().stream().mapToDouble(el -> el).max().orElse(0);
        double min = valueMap.values().stream().mapToDouble(el -> el).min().orElse(0);
        for (BiIndexData d : biIndexData) {
            JSONArray polygon = biIndexDataService.changeMultipolygon(d.getMultipolygon());
            d.setMultipolygonArray(polygon);
            double value = 0;
            for (BiOperatedData store : storeList) {
                //如果该店铺在网格内，计算这个店铺的消费券、客流量、客单价数据
                if (withinPolygonRange(polygon, store.getLng(), store.getLat())) {
                    log.info("withinPolygonRange:"+store.getStoreName());
                    Double val = 0d;
                    if(type.startsWith("coupon")){
                        //优惠券核销量
                        val = valueMap.get(store.getStoreId());
                    }else if(type.equals("passengerFlow")){
                        //客流量
                        val = valueMap.get(store.getStoreName());
                    }else if(type.equals("passengerPrice")){
                        //客单价 平均值
                        val = valueMap.get(store.getStoreName());
                    }
                    if (val != null) {
                        value += val;
                    }
                }
            }
            d.setValue(Double.toString(value));
            String colorByValue = GridColorUtils.getColorByValue(colorLevel, max, min, d.getValue());
            d.setColor(colorByValue);
        }
        // 整理网格数据
        log.info("开始整理网格数据");

        result.put("list",biIndexData);
        result.put("colorLevel", colorLevel);
        result.put("valueRange", GridColorUtils.getValueRange(colorLevel,max,min));
        result.put("orgCode", orgCodeString);
        result.put("name", name);
        log.info("存在数据的网格："+biIndexData.size());
        return result;
    }

    /**
     * 判断点位是否在区域范围内
     * @return
     */
    private boolean withinPolygonRange(JSONArray polygon,String lngStr,String latStr){
        BigDecimal[] polygonArrayRange = DistanceHelper.getPolygonArrayRange(polygon);
        if (polygonArrayRange != null) {
            BigDecimal lng1 = polygonArrayRange[0];
            BigDecimal lng2 = polygonArrayRange[1];
            BigDecimal lat1 = polygonArrayRange[2];
            BigDecimal lat2 = polygonArrayRange[3];
            BigDecimal lng = new BigDecimal(lngStr);
            BigDecimal lat = new BigDecimal(latStr);
            if(lng.compareTo(lng1)>=0 && lng.compareTo(lng2)<= 0){
                if(lat.compareTo(lat1)>=0 && lat.compareTo(lat2)<= 0){
                    return true;
                }
            }
        }
        return false;
    }

}
